D:\a\tools.proto\tools.proto\compiler\src\compiler\message.rs
Line | Count | Source |
1 | | // Copyright (c) 2024, BlockProject 3D |
2 | | // |
3 | | // All rights reserved. |
4 | | // |
5 | | // Redistribution and use in source and binary forms, with or without modification, |
6 | | // are permitted provided that the following conditions are met: |
7 | | // |
8 | | // * Redistributions of source code must retain the above copyright notice, |
9 | | // this list of conditions and the following disclaimer. |
10 | | // * Redistributions in binary form must reproduce the above copyright notice, |
11 | | // this list of conditions and the following disclaimer in the documentation |
12 | | // and/or other materials provided with the distribution. |
13 | | // * Neither the name of BlockProject 3D nor the names of its contributors |
14 | | // may be used to endorse or promote products derived from this software |
15 | | // without specific prior written permission. |
16 | | // |
17 | | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
18 | | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
19 | | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
20 | | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
21 | | // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
22 | | // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
23 | | // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
24 | | // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
25 | | // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
26 | | // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
27 | | // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
28 | | |
29 | | use crate::compiler::error::Error; |
30 | | use crate::compiler::structure::{FixedFieldType, Structure}; |
31 | | use crate::compiler::union::Union; |
32 | | use crate::compiler::util::store::name_index; |
33 | | use crate::compiler::util::types::{Name, PtrKey}; |
34 | | use crate::compiler::Protocol; |
35 | | use crate::model::message::MessageFieldValue; |
36 | | use crate::model::protocol::{Description, Endianness}; |
37 | | use crate::model::structure::StructFieldRaw; |
38 | | use bp3d_debug::{error, trace}; |
39 | | use std::cell::Cell; |
40 | | use std::fmt::{Display, Formatter}; |
41 | | use std::rc::Rc; |
42 | | use crate::compiler::builder::FieldBuilder; |
43 | | |
44 | | #[derive(Clone, Debug)] |
45 | | pub enum Referenced { |
46 | | Struct(Rc<Structure>), |
47 | | Message(Rc<Message>), |
48 | | } |
49 | | |
50 | | impl Name for Referenced { |
51 | 32 | fn name(&self) -> &str { |
52 | 32 | match self { |
53 | 24 | Referenced::Struct(v) => &v.name, |
54 | 8 | Referenced::Message(v) => &v.name, |
55 | | } |
56 | 32 | } |
57 | | } |
58 | | |
59 | | impl PtrKey for Referenced { |
60 | 63 | fn ptr_key(&self) -> usize { |
61 | 63 | match self { |
62 | 54 | Referenced::Struct(v) => v.ptr_key(), |
63 | 9 | Referenced::Message(v) => v.ptr_key(), |
64 | | } |
65 | 63 | } |
66 | | } |
67 | | |
68 | | impl Referenced { |
69 | 58 | pub fn lookup(proto: &Protocol, reference_name: &str) -> Option<Self> { |
70 | 58 | proto |
71 | 58 | .structs |
72 | 58 | .get(reference_name) |
73 | 58 | .map(|v| Referenced::Struct(v.clone())39 ) |
74 | 58 | .or_else(|| proto.messages.get(reference_name).map(19 |v| Referenced::Message(v.clone())19 )19 ) |
75 | 58 | } |
76 | | } |
77 | | |
78 | | #[derive(Clone, Debug)] |
79 | | pub struct FixedContainerField { |
80 | | pub ty: FixedFieldType, |
81 | | pub item_type: Rc<Structure>, |
82 | | } |
83 | | |
84 | | impl Display for FixedContainerField { |
85 | 10 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
86 | 10 | write!(f, "FixedContainer<{}, Len = {}>", self.item_type.name, self.ty) |
87 | 10 | } |
88 | | } |
89 | | |
90 | | #[derive(Clone, Debug)] |
91 | | pub struct SizedBufferField { |
92 | | pub ty: FixedFieldType, |
93 | | } |
94 | | |
95 | | impl Display for SizedBufferField { |
96 | 8 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
97 | 8 | write!(f, "Varbuf<{}>", self.ty) |
98 | 8 | } |
99 | | } |
100 | | |
101 | | #[derive(Clone, Debug)] |
102 | | pub struct ContainerField { |
103 | | pub ty: FixedFieldType, |
104 | | pub item_type: Rc<Message>, |
105 | | pub nested: bool, |
106 | | } |
107 | | |
108 | | impl Display for ContainerField { |
109 | 21 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
110 | 21 | write!(f, "Container<{}, Len = {}>", self.item_type.name(), self.ty) |
111 | 21 | } |
112 | | } |
113 | | |
114 | | #[derive(Clone, Debug)] |
115 | | pub struct SizedContainerField { |
116 | | pub ty: FixedFieldType, |
117 | | pub item_type: Rc<Message>, |
118 | | pub size_ty: FixedFieldType, |
119 | | } |
120 | | |
121 | | impl Display for SizedContainerField { |
122 | 7 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
123 | 7 | write!( |
124 | 7 | f, |
125 | 7 | "SizedContainer<{}, Len = {}, Size = {}>", |
126 | 7 | self.item_type.name(), |
127 | 7 | self.ty, |
128 | 7 | self.size_ty |
129 | 7 | ) |
130 | 7 | } |
131 | | } |
132 | | |
133 | | #[derive(Clone, Debug)] |
134 | | pub struct FixedField { |
135 | | pub ty: FixedFieldType, |
136 | | } |
137 | | |
138 | | impl Display for FixedField { |
139 | 8 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
140 | 8 | write!(f, "{}", self.ty) |
141 | 8 | } |
142 | | } |
143 | | |
144 | | #[derive(Clone, Debug)] |
145 | | pub struct UnionField { |
146 | | pub r: Rc<Union>, |
147 | | } |
148 | | |
149 | | impl Display for UnionField { |
150 | 8 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
151 | 8 | f.write_str(self.r.name()) |
152 | 8 | } |
153 | | } |
154 | | |
155 | | #[derive(Clone, Debug)] |
156 | | pub enum FieldType { |
157 | | Fixed(FixedField), |
158 | | Ref(Referenced), |
159 | | |
160 | | /// A buffer field is a field which has a known size which can be determined at runtime |
161 | | /// (ex: a null-terminated string). |
162 | | Buffer, |
163 | | |
164 | | /// A sized buffer field is a field which has a known size field based on a configurable type |
165 | | /// (ex: a Varchar). |
166 | | SizedBuffer(SizedBufferField), |
167 | | |
168 | | /// A fixed container is a container which can store only fixed size elements (structures). |
169 | | FixedContainer(FixedContainerField), |
170 | | |
171 | | Union(UnionField), |
172 | | |
173 | | /// A container can store dynamically sized elements. |
174 | | Container(ContainerField), |
175 | | |
176 | | /// A container which has an additional configurable size field to detect the size in bytes of |
177 | | /// the container. |
178 | | SizedContainer(SizedContainerField), |
179 | | |
180 | | Payload, |
181 | | } |
182 | | |
183 | | impl Display for FieldType { |
184 | 106 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
185 | 106 | match self { |
186 | 8 | FieldType::Fixed(v) => v.fmt(f), |
187 | 24 | FieldType::Ref(v) => f.write_str(v.name()), |
188 | 20 | FieldType::Buffer => f.write_str("Buffer"), |
189 | 8 | FieldType::SizedBuffer(v) => v.fmt(f), |
190 | 10 | FieldType::FixedContainer(v) => v.fmt(f), |
191 | 8 | FieldType::Union(v) => v.fmt(f), |
192 | 21 | FieldType::Container(v) => v.fmt(f), |
193 | 7 | FieldType::SizedContainer(v) => v.fmt(f), |
194 | 0 | FieldType::Payload => f.write_str("Bytes"), |
195 | | } |
196 | 106 | } |
197 | | } |
198 | | |
199 | | impl FieldType { |
200 | 49 | pub fn is_message_reference(&self) -> bool { |
201 | 49 | match self { |
202 | 12 | FieldType::Ref(v) => match v { |
203 | 9 | Referenced::Struct(_) => false, |
204 | 3 | Referenced::Message(_) => true, |
205 | | }, |
206 | 37 | _ => false, |
207 | | } |
208 | 49 | } |
209 | | |
210 | 19 | pub fn is_union(&self) -> bool { |
211 | 19 | matches!6 (self, FieldType::Union(_)) |
212 | 19 | } |
213 | | |
214 | 53 | pub fn is_string(&self) -> bool { |
215 | 53 | matches!35 (self, FieldType::SizedBuffer(_) | FieldType::Buffer) |
216 | 53 | } |
217 | | } |
218 | | |
219 | | #[derive(Copy, Clone, Debug)] |
220 | | pub struct SizeInfo { |
221 | | pub is_dyn_sized: bool, |
222 | | pub is_element_dyn_sized: bool, |
223 | | } |
224 | | |
225 | | #[derive(Clone, Debug)] |
226 | | pub struct HeaderField { |
227 | | pub name: String, |
228 | | pub index: usize, |
229 | | } |
230 | | |
231 | | impl HeaderField { |
232 | 62 | fn from_model(header: Option<String>, fields: &[Field]) -> Result<(Option<Self>, Option<&Field>), Error> { |
233 | 62 | match header { |
234 | 10 | Some(header) => { |
235 | 10 | let (index, field9 ) = fields |
236 | 10 | .iter() |
237 | 10 | .enumerate() |
238 | 10 | .find_map(|(k, v)| if v.name == header9 { Some((k, v))9 } else { None0 }9 ) Branch (238:43): [True: 9, False: 0]
Branch (238:43): [Folded - Ignored]
|
239 | 10 | .ok_or(Error::UndefinedReference(header))?1 ; |
240 | 9 | let header = HeaderField { |
241 | 9 | index, |
242 | 9 | name: field.name.clone() |
243 | 9 | }; |
244 | 9 | Ok((Some(header), Some(field))) |
245 | | } |
246 | 52 | None => Ok((None, None)), |
247 | | } |
248 | 62 | } |
249 | | } |
250 | | |
251 | | #[derive(Clone, Debug)] |
252 | | pub struct Field { |
253 | | pub name: String, |
254 | | pub header: Option<HeaderField>, |
255 | | pub ty: FieldType, |
256 | | pub optional: bool, |
257 | | pub size: SizeInfo, |
258 | | pub endianness: Endianness, |
259 | | pub description: Option<Description>, |
260 | | pub codec: Option<String>, |
261 | | } |
262 | | |
263 | | impl Display for Field { |
264 | 106 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { |
265 | 106 | match (self.optional, &self.codec) { |
266 | 4 | (true, None) => write!(f, "{}: {}?, {} endian", self.name, self.ty, self.endianness), |
267 | 36 | (false, None) => write!(f, "{}: {}, {} endian", self.name, self.ty, self.endianness), |
268 | 11 | (true, Some(v)) => write!(f, "{}: {} ({})?, {} endian", self.name, v, self.ty, self.endianness), |
269 | 55 | (false, Some(v)) => write!(f, "{}: {} ({}), {} endian", self.name, v, self.ty, self.endianness) |
270 | | } |
271 | 106 | } |
272 | | } |
273 | | |
274 | | impl Field { |
275 | 227 | pub fn codec(&self) -> &str { |
276 | 227 | self.codec.as_deref().unwrap_or("base") |
277 | 227 | } |
278 | | |
279 | 64 | fn from_model( |
280 | 64 | proto: &Protocol, |
281 | 64 | unsorted: &[Field], |
282 | 64 | has_headers: bool, |
283 | 64 | value: crate::model::message::MessageField, |
284 | 64 | ) -> Result<Self, Error> { |
285 | 64 | if (value.value.is_none() && value.item_type.is_none()16 ) || (value.value.is_some()63 && value.item_type.is_some()48 ) Branch (285:13): [True: 16, False: 48]
Branch (285:38): [True: 1, False: 15]
Branch (285:69): [True: 48, False: 15]
Branch (285:94): [True: 1, False: 47]
Branch (285:13): [Folded - Ignored]
Branch (285:38): [Folded - Ignored]
Branch (285:69): [Folded - Ignored]
Branch (285:94): [Folded - Ignored]
|
286 | | { |
287 | 2 | return Err(Error::BadFieldType); |
288 | 62 | } |
289 | 62 | let (header, header_field61 ) = HeaderField::from_model(value.header, unsorted)?1 ; |
290 | 61 | if let Some(field9 ) = header_field { Branch (290:16): [True: 9, False: 52]
Branch (290:16): [Folded - Ignored]
|
291 | 8 | match &field.ty { |
292 | 8 | FieldType::Ref(Referenced::Struct(v)) => v.set_used_in_header(), |
293 | 1 | v => { |
294 | 1 | error!("Invalid header field type, expected struct reference, got field type {:?}", v); |
295 | 1 | return Err(Error::InvalidHeaderType); |
296 | | } |
297 | | } |
298 | 52 | } |
299 | 60 | let builder = FieldBuilder::new(value.name, value.optional.unwrap_or_default(), proto.endianness) |
300 | 60 | .description(value.description).header(header).codec(value.codec); |
301 | 60 | if let Some(info45 ) = value.value { Branch (301:16): [True: 45, False: 15]
Branch (301:16): [Folded - Ignored]
|
302 | 45 | match info { |
303 | | MessageFieldValue::List { |
304 | 16 | max_len, |
305 | 16 | item_type, |
306 | 16 | max_size, |
307 | 16 | nested, |
308 | 16 | } => { |
309 | 16 | if max_len == 0 { Branch (309:24): [True: 1, False: 15]
Branch (309:24): [Folded - Ignored]
|
310 | 1 | return Err(Error::ZeroArray); |
311 | 15 | } |
312 | 15 | if builder.has_codec() { Branch (312:24): [True: 0, False: 15]
Branch (312:24): [Folded - Ignored]
|
313 | 0 | return Err(Error::ForbiddenCodec(builder.name().into())); |
314 | 15 | } |
315 | 15 | let r = Referenced::lookup(proto, &item_type).ok_or(Error::UndefinedReference(item_type))?0 ; |
316 | 15 | let ty = FixedFieldType::from_max_value(max_len)?0 ; |
317 | 15 | match r { |
318 | 4 | Referenced::Struct(item_type) => Ok(builder.codec(Some("list".into())).build(FieldType::FixedContainer(FixedContainerField { item_type, ty }))), |
319 | 11 | Referenced::Message(item_type) => { |
320 | 11 | if let Some(max_size3 ) = max_size { Branch (320:36): [True: 3, False: 8]
Branch (320:36): [Folded - Ignored]
|
321 | 3 | if max_size == 0 { Branch (321:36): [True: 1, False: 2]
Branch (321:36): [Folded - Ignored]
|
322 | 1 | return Err(Error::ZeroArray); |
323 | 2 | } |
324 | 2 | let size_ty = FixedFieldType::from_max_value(max_size)?0 ; |
325 | 2 | Ok( |
326 | 2 | builder |
327 | 2 | .codec(Some("list".into())) |
328 | 2 | .size_info(SizeInfo { |
329 | 2 | is_element_dyn_sized: false, |
330 | 2 | is_dyn_sized: true |
331 | 2 | }) |
332 | 2 | .build(FieldType::SizedContainer(SizedContainerField { ty, item_type, size_ty })) |
333 | 2 | ) |
334 | | } else { |
335 | 8 | item_type.embedded.set(true); |
336 | 8 | Ok( |
337 | 8 | builder |
338 | 8 | .codec(Some("list".into())) |
339 | 8 | .dynamic_size() |
340 | 8 | .build(FieldType::Container(ContainerField { |
341 | 8 | ty, |
342 | 8 | item_type, |
343 | 8 | nested: nested.unwrap_or_default(), |
344 | 8 | })) |
345 | 8 | ) |
346 | | } |
347 | | } |
348 | | } |
349 | | }, |
350 | | MessageFieldValue::Container { |
351 | 2 | max_len, |
352 | 2 | item_type, |
353 | 2 | max_size, |
354 | 2 | nested, |
355 | 2 | } => { |
356 | 2 | if max_len == 0 { Branch (356:24): [True: 0, False: 2]
Branch (356:24): [Folded - Ignored]
|
357 | 0 | return Err(Error::ZeroArray); |
358 | 2 | } |
359 | 2 | let r = Referenced::lookup(proto, &item_type).ok_or(Error::UndefinedReference(item_type))?0 ; |
360 | 2 | let ty = FixedFieldType::from_max_value(max_len)?0 ; |
361 | 2 | match r { |
362 | 0 | Referenced::Struct(item_type) => Ok(builder.build(FieldType::FixedContainer(FixedContainerField { item_type, ty }))), |
363 | 2 | Referenced::Message(item_type) => { |
364 | 2 | if let Some(max_size1 ) = max_size { Branch (364:36): [True: 1, False: 1]
Branch (364:36): [Folded - Ignored]
|
365 | 1 | if max_size == 0 { Branch (365:36): [True: 0, False: 1]
Branch (365:36): [Folded - Ignored]
|
366 | 0 | return Err(Error::ZeroArray); |
367 | 1 | } |
368 | 1 | let size_ty = FixedFieldType::from_max_value(max_size)?0 ; |
369 | 1 | Ok( |
370 | 1 | builder |
371 | 1 | .size_info(SizeInfo { |
372 | 1 | is_element_dyn_sized: false, |
373 | 1 | is_dyn_sized: true |
374 | 1 | }) |
375 | 1 | .build(FieldType::SizedContainer(SizedContainerField { ty, item_type, size_ty })) |
376 | 1 | ) |
377 | | } else { |
378 | 1 | item_type.embedded.set(true); |
379 | 1 | Ok( |
380 | 1 | builder |
381 | 1 | .dynamic_size() |
382 | 1 | .build(FieldType::Container(ContainerField { |
383 | 1 | ty, |
384 | 1 | item_type, |
385 | 1 | nested: nested.unwrap_or_default(), |
386 | 1 | })) |
387 | 1 | ) |
388 | | } |
389 | | } |
390 | | } |
391 | | }, |
392 | 14 | MessageFieldValue::String { max_len } => { |
393 | 14 | if builder.has_codec() { Branch (393:24): [True: 0, False: 14]
Branch (393:24): [Folded - Ignored]
|
394 | 0 | return Err(Error::ForbiddenCodec(builder.name().into())); |
395 | 14 | } |
396 | 14 | match max_len { |
397 | 10 | None => Ok(builder.codec(Some("string".into())).build(FieldType::Buffer)), |
398 | 4 | Some(max_len) => { |
399 | 4 | if max_len == 0 { Branch (399:32): [True: 1, False: 3]
Branch (399:32): [Folded - Ignored]
|
400 | 1 | return Err(Error::ZeroArray); |
401 | 3 | } |
402 | 3 | let ty = FixedFieldType::from_max_value(max_len)?0 ; |
403 | 3 | Ok(builder.codec(Some("string".into())).build(FieldType::SizedBuffer(SizedBufferField { ty }))) |
404 | | } |
405 | | } |
406 | | }, |
407 | 2 | MessageFieldValue::Buffer { max_len } => { |
408 | 2 | match max_len { |
409 | 0 | None => Ok(builder.build(FieldType::Buffer)), |
410 | 2 | Some(max_len) => { |
411 | 2 | if max_len == 0 { Branch (411:32): [True: 0, False: 2]
Branch (411:32): [Folded - Ignored]
|
412 | 0 | return Err(Error::ZeroArray); |
413 | 2 | } |
414 | 2 | let ty = FixedFieldType::from_max_value(max_len)?0 ; |
415 | 2 | Ok(builder.build(FieldType::SizedBuffer(SizedBufferField { ty }))) |
416 | | } |
417 | | } |
418 | | }, |
419 | 7 | MessageFieldValue::Union { name } => { |
420 | 7 | let r6 = proto.unions.get(&name).ok_or(Error::UndefinedReference(name))?1 ; |
421 | 6 | let header_field5 = header_field.ok_or(Error::MissingHeaderForUnion)?1 ; |
422 | 5 | match &header_field.ty { |
423 | 5 | FieldType::Ref(Referenced::Struct(v)) => { |
424 | 5 | if !Rc::ptr_eq(&r.discriminant.root, v) { Branch (424:32): [True: 1, False: 4]
Branch (424:32): [Folded - Ignored]
|
425 | 1 | error!( |
426 | 1 | "Union discriminant type mismatch, expected {}, got {}", |
427 | 1 | v.name, r.discriminant.root.name |
428 | 1 | ); |
429 | 1 | return Err(Error::UnionTypeMismatch); |
430 | 4 | } |
431 | | }, |
432 | 0 | _ => unreachable!() |
433 | | } |
434 | 4 | if value.optional.unwrap_or_default() { Branch (434:24): [True: 0, False: 4]
Branch (434:24): [Folded - Ignored]
|
435 | 0 | eprintln!("WARNING: ignoring unsupported optional flag on union message field!"); |
436 | 4 | } |
437 | 4 | Ok(builder.size_info(r.size).build(FieldType::Union(UnionField { r: r.clone() }))) |
438 | | } |
439 | 0 | MessageFieldValue::Payload => Ok(builder.dynamic_size().build(FieldType::Payload)), |
440 | 4 | MessageFieldValue::Unsigned { bits } => { |
441 | 4 | let ty = FixedFieldType::from_model(StructFieldRaw::Unsigned { bits })?0 ; |
442 | 4 | Ok(builder.fixed_size().build(FieldType::Fixed(FixedField { ty }))) |
443 | | } |
444 | | } |
445 | | } else { |
446 | 15 | let item_type = unsafe { value.item_type.unwrap_unchecked() }; |
447 | 15 | let r = Referenced::lookup(proto, &item_type).ok_or(Error::UndefinedReference(item_type))?0 ; |
448 | 15 | match r { |
449 | 13 | Referenced::Struct(r) => { |
450 | 13 | let is_single = r.fields.len() == 1; |
451 | 13 | let is_fixed = r.fields[0].ty.as_fixed().is_some(); |
452 | 13 | let is_none = r.fields[0].ty.as_fixed().map(|v| v.raw.is_none()).unwrap_or_default(); |
453 | 13 | let is_byte_aligned = r.fields[0].loc.bit_size % 8 == 0; |
454 | 13 | trace!({has_headers} {is_single} {is_fixed} {is_none} {is_byte_aligned}, "Found struct reference: {}", r.name); |
455 | 13 | if !has_headers && is_single6 && is_fixed2 && is_none2 && is_byte_aligned2 { Branch (455:24): [True: 6, False: 7]
Branch (455:40): [True: 2, False: 4]
Branch (455:53): [True: 2, False: 0]
Branch (455:65): [True: 2, False: 0]
Branch (455:76): [True: 2, False: 0]
Branch (455:24): [Folded - Ignored]
Branch (455:40): [Folded - Ignored]
Branch (455:53): [Folded - Ignored]
Branch (455:65): [Folded - Ignored]
Branch (455:76): [Folded - Ignored]
|
456 | 2 | let fixed = unsafe { r.fields[0].ty.as_fixed().unwrap_unchecked() }; |
457 | 2 | Ok(builder.fixed_size().build(FieldType::Fixed(FixedField { ty: fixed.bits_type }))) |
458 | | } else { |
459 | 11 | Ok(builder.fixed_size().build(FieldType::Ref(Referenced::Struct(r)))) |
460 | | } |
461 | | } |
462 | 2 | Referenced::Message(r) => Ok(builder.size_info(r.size).build(FieldType::Ref(Referenced::Message(r)))) |
463 | | } |
464 | | } |
465 | 64 | } |
466 | | } |
467 | | |
468 | | #[derive(Clone, Debug)] |
469 | | pub struct Message { |
470 | | pub name: String, |
471 | | pub description: Option<Description>, |
472 | | pub fields: Vec<Field>, |
473 | | pub size: SizeInfo, |
474 | | embedded: Cell<bool>, |
475 | | } |
476 | | |
477 | | impl Message { |
478 | 28 | pub(crate) fn is_embedded(&self) -> bool { |
479 | 28 | self.embedded.get() |
480 | 28 | } |
481 | | |
482 | 39 | pub fn from_model(proto: &Protocol, value: crate::model::message::Message) -> Result<Message, Error> { |
483 | 39 | let mut fields = Vec::with_capacity(value.fields.len()); |
484 | 39 | let mut dyn_sized_elem_count = 0; |
485 | 39 | let mut is_dyn_sized = false; |
486 | 63 | let has_headers = value.fields.iter().any(|v| v.header.is_some())39 ; |
487 | 93 | for v64 in value.fields { |
488 | 64 | let field54 = Field::from_model(proto, &fields, has_headers, v)?10 ; |
489 | 54 | if field.size.is_dyn_sized { Branch (489:16): [True: 37, False: 17]
Branch (489:16): [Folded - Ignored]
|
490 | 37 | is_dyn_sized = true; |
491 | 37 | }17 |
492 | 54 | if dyn_sized_elem_count > 0 && (field.size.is_dyn_sized0 || field.size.is_element_dyn_sized0 ) { Branch (492:16): [True: 0, False: 54]
Branch (492:45): [True: 0, False: 0]
Branch (492:72): [True: 0, False: 0]
Branch (492:16): [Folded - Ignored]
Branch (492:45): [Folded - Ignored]
Branch (492:72): [Folded - Ignored]
|
493 | 0 | return Err(Error::VarsizeAfterPayload); |
494 | 54 | } |
495 | 54 | if field.size.is_element_dyn_sized { Branch (495:16): [True: 9, False: 45]
Branch (495:16): [Folded - Ignored]
|
496 | 9 | dyn_sized_elem_count += 1; |
497 | 45 | } |
498 | 54 | if dyn_sized_elem_count > 1 { Branch (498:16): [True: 0, False: 54]
Branch (498:16): [Folded - Ignored]
|
499 | 0 | return Err(Error::MultiPayload); |
500 | 54 | } |
501 | 54 | fields.push(field); |
502 | | } |
503 | 29 | Ok(Message { |
504 | 29 | name: value.name, |
505 | 29 | description: value.description, |
506 | 29 | fields, |
507 | 29 | size: SizeInfo { |
508 | 29 | is_dyn_sized, |
509 | 29 | is_element_dyn_sized: dyn_sized_elem_count > 0, |
510 | 29 | }, |
511 | 29 | embedded: Cell::new(false), |
512 | 29 | }) |
513 | 39 | } |
514 | | } |
515 | | |
516 | | name_index!(Message => name); |